home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / generic / tclTimer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  30.7 KB  |  1,109 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclTimer.c --
  3.  *
  4.  *    This file provides timer event management facilities for Tcl,
  5.  *    including the "after" command.
  6.  *
  7.  * Copyright (c) 1997 by Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tclTimer.c 1.9 97/07/29 16:21:53
  13.  */
  14.  
  15. #include "tclInt.h"
  16. #include "tclPort.h"
  17.  
  18. /*
  19.  * This flag indicates whether this module has been initialized.
  20.  */
  21.  
  22. static int initialized = 0;
  23.  
  24. /*
  25.  * For each timer callback that's pending there is one record of the following
  26.  * type.  The normal handlers (created by Tcl_CreateTimerHandler) are chained
  27.  * together in a list sorted by time (earliest event first).
  28.  */
  29.  
  30. typedef struct TimerHandler {
  31.     Tcl_Time time;            /* When timer is to fire. */
  32.     Tcl_TimerProc *proc;        /* Procedure to call. */
  33.     ClientData clientData;        /* Argument to pass to proc. */
  34.     Tcl_TimerToken token;        /* Identifies handler so it can be
  35.                      * deleted. */
  36.     struct TimerHandler *nextPtr;    /* Next event in queue, or NULL for
  37.                      * end of queue. */
  38. } TimerHandler;
  39.  
  40. static TimerHandler *firstTimerHandlerPtr = NULL;
  41.                     /* First event in queue. */
  42. static int lastTimerId;            /* Timer identifier of most recently
  43.                      * created timer. */
  44. static int timerPending;        /* 1 if a timer event is in the queue. */
  45.  
  46. /*
  47.  * The data structure below is used by the "after" command to remember
  48.  * the command to be executed later.  All of the pending "after" commands
  49.  * for an interpreter are linked together in a list.
  50.  */
  51.  
  52. typedef struct AfterInfo {
  53.     struct AfterAssocData *assocPtr;
  54.                 /* Pointer to the "tclAfter" assocData for
  55.                  * the interp in which command will be
  56.                  * executed. */
  57.     char *command;        /* Command to execute.  Malloc'ed, so must
  58.                  * be freed when structure is deallocated. */
  59.     int id;            /* Integer identifier for command;  used to
  60.                  * cancel it. */
  61.     Tcl_TimerToken token;    /* Used to cancel the "after" command.  NULL
  62.                  * means that the command is run as an
  63.                  * idle handler rather than as a timer
  64.                  * handler.  NULL means this is an "after
  65.                  * idle" handler rather than a
  66.                                  * timer handler. */
  67.     struct AfterInfo *nextPtr;    /* Next in list of all "after" commands for
  68.                  * this interpreter. */
  69. } AfterInfo;
  70.  
  71. /*
  72.  * One of the following structures is associated with each interpreter
  73.  * for which an "after" command has ever been invoked.  A pointer to
  74.  * this structure is stored in the AssocData for the "tclAfter" key.
  75.  */
  76.  
  77. typedef struct AfterAssocData {
  78.     Tcl_Interp *interp;        /* The interpreter for which this data is
  79.                  * registered. */
  80.     AfterInfo *firstAfterPtr;    /* First in list of all "after" commands
  81.                  * still pending for this interpreter, or
  82.                  * NULL if none. */
  83. } AfterAssocData;
  84.  
  85. /*
  86.  * There is one of the following structures for each of the
  87.  * handlers declared in a call to Tcl_DoWhenIdle.  All of the
  88.  * currently-active handlers are linked together into a list.
  89.  */
  90.  
  91. typedef struct IdleHandler {
  92.     Tcl_IdleProc (*proc);    /* Procedure to call. */
  93.     ClientData clientData;    /* Value to pass to proc. */
  94.     int generation;        /* Used to distinguish older handlers from
  95.                  * recently-created ones. */
  96.     struct IdleHandler *nextPtr;/* Next in list of active handlers. */
  97. } IdleHandler;
  98.  
  99. static IdleHandler *idleList;
  100.                 /* First in list of all idle handlers. */
  101. static IdleHandler *lastIdlePtr;
  102.                 /* Last in list (or NULL for empty list). */
  103. static int idleGeneration;    /* Used to fill in the "generation" fields
  104.                  * of IdleHandler structures.  Increments
  105.                  * each time Tcl_DoOneEvent starts calling
  106.                  * idle handlers, so that all old handlers
  107.                  * can be called without calling any of the
  108.                  * new ones created by old ones. */
  109.  
  110. /*
  111.  * Prototypes for procedures referenced only in this file:
  112.  */
  113.  
  114. static void        AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
  115.                 Tcl_Interp *interp));
  116. static void        AfterProc _ANSI_ARGS_((ClientData clientData));
  117. static void        FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
  118. static AfterInfo *    GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
  119.                 char *string));
  120. static void        InitTimer _ANSI_ARGS_((void));
  121. static void        TimerExitProc _ANSI_ARGS_((ClientData clientData));
  122. static int        TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  123.                 int flags));
  124. static void        TimerCheckProc _ANSI_ARGS_((ClientData clientData,
  125.                 int flags));
  126. static void        TimerSetupProc _ANSI_ARGS_((ClientData clientData,
  127.                 int flags));
  128.  
  129. /*
  130.  *----------------------------------------------------------------------
  131.  *
  132.  * InitTimer --
  133.  *
  134.  *    This function initializes the timer module.
  135.  *
  136.  * Results:
  137.  *    None.
  138.  *
  139.  * Side effects:
  140.  *    Registers the idle and timer event sources.
  141.  *
  142.  *----------------------------------------------------------------------
  143.  */
  144.  
  145. static void
  146. InitTimer()
  147. {
  148.     initialized = 1;
  149.     lastTimerId = 0;
  150.     timerPending = 0;
  151.     idleGeneration = 0;
  152.     firstTimerHandlerPtr = NULL;
  153.     lastIdlePtr = NULL;
  154.     idleList = NULL;
  155.  
  156.     Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
  157.     Tcl_CreateExitHandler(TimerExitProc, NULL);
  158. }
  159.  
  160. /*
  161.  *----------------------------------------------------------------------
  162.  *
  163.  * TimerExitProc --
  164.  *
  165.  *    This function is call at exit or unload time to remove the
  166.  *    timer and idle event sources.
  167.  *
  168.  * Results:
  169.  *    None.
  170.  *
  171.  * Side effects:
  172.  *    Removes the timer and idle event sources.
  173.  *
  174.  *----------------------------------------------------------------------
  175.  */
  176.  
  177. static void
  178. TimerExitProc(clientData)
  179.     ClientData clientData;    /* Not used. */
  180. {
  181.     Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
  182.     initialized = 0;
  183. }
  184.  
  185. /*
  186.  *--------------------------------------------------------------
  187.  *
  188.  * Tcl_CreateTimerHandler --
  189.  *
  190.  *    Arrange for a given procedure to be invoked at a particular
  191.  *    time in the future.
  192.  *
  193.  * Results:
  194.  *    The return value is a token for the timer event, which
  195.  *    may be used to delete the event before it fires.
  196.  *
  197.  * Side effects:
  198.  *    When milliseconds have elapsed, proc will be invoked
  199.  *    exactly once.
  200.  *
  201.  *--------------------------------------------------------------
  202.  */
  203.  
  204. Tcl_TimerToken
  205. Tcl_CreateTimerHandler(milliseconds, proc, clientData)
  206.     int milliseconds;        /* How many milliseconds to wait
  207.                  * before invoking proc. */
  208.     Tcl_TimerProc *proc;    /* Procedure to invoke. */
  209.     ClientData clientData;    /* Arbitrary data to pass to proc. */
  210. {
  211.     register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
  212.     Tcl_Time time;
  213.  
  214.     if (!initialized) {
  215.     InitTimer();
  216.     }
  217.  
  218.     timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
  219.  
  220.     /*
  221.      * Compute when the event should fire.
  222.      */
  223.  
  224.     TclpGetTime(&time);
  225.     timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
  226.     timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
  227.     if (timerHandlerPtr->time.usec >= 1000000) {
  228.     timerHandlerPtr->time.usec -= 1000000;
  229.     timerHandlerPtr->time.sec += 1;
  230.     }
  231.     
  232.     /*
  233.      * Fill in other fields for the event.
  234.      */
  235.  
  236.     timerHandlerPtr->proc = proc;
  237.     timerHandlerPtr->clientData = clientData;
  238.     lastTimerId++;
  239.     timerHandlerPtr->token = (Tcl_TimerToken) lastTimerId;
  240.  
  241.     /*
  242.      * Add the event to the queue in the correct position
  243.      * (ordered by event firing time).
  244.      */
  245.  
  246.     for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
  247.         prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
  248.     if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
  249.         || ((tPtr2->time.sec == timerHandlerPtr->time.sec)
  250.         && (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
  251.         break;
  252.     }
  253.     }
  254.     timerHandlerPtr->nextPtr = tPtr2;
  255.     if (prevPtr == NULL) {
  256.     firstTimerHandlerPtr = timerHandlerPtr;
  257.     } else {
  258.     prevPtr->nextPtr = timerHandlerPtr;
  259.     }
  260.  
  261.     TimerSetupProc(NULL, TCL_ALL_EVENTS);
  262.     return timerHandlerPtr->token;
  263. }
  264.  
  265. /*
  266.  *--------------------------------------------------------------
  267.  *
  268.  * Tcl_DeleteTimerHandler --
  269.  *
  270.  *    Delete a previously-registered timer handler.
  271.  *
  272.  * Results:
  273.  *    None.
  274.  *
  275.  * Side effects:
  276.  *    Destroy the timer callback identified by TimerToken,
  277.  *    so that its associated procedure will not be called.
  278.  *    If the callback has already fired, or if the given
  279.  *    token doesn't exist, then nothing happens.
  280.  *
  281.  *--------------------------------------------------------------
  282.  */
  283.  
  284. void
  285. Tcl_DeleteTimerHandler(token)
  286.     Tcl_TimerToken token;    /* Result previously returned by
  287.                  * Tcl_DeleteTimerHandler. */
  288. {
  289.     register TimerHandler *timerHandlerPtr, *prevPtr;
  290.  
  291.     for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;
  292.         timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
  293.         timerHandlerPtr = timerHandlerPtr->nextPtr) {
  294.     if (timerHandlerPtr->token != token) {
  295.         continue;
  296.     }
  297.     if (prevPtr == NULL) {
  298.         firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
  299.     } else {
  300.         prevPtr->nextPtr = timerHandlerPtr->nextPtr;
  301.     }
  302.     ckfree((char *) timerHandlerPtr);
  303.     return;
  304.     }
  305. }
  306.  
  307. /*
  308.  *----------------------------------------------------------------------
  309.  *
  310.  * TimerSetupProc --
  311.  *
  312.  *    This function is called by Tcl_DoOneEvent to setup the timer
  313.  *    event source for before blocking.  This routine checks both the
  314.  *    idle and after timer lists.
  315.  *
  316.  * Results:
  317.  *    None.
  318.  *
  319.  * Side effects:
  320.  *    May update the maximum notifier block time.
  321.  *
  322.  *----------------------------------------------------------------------
  323.  */
  324.  
  325. static void
  326. TimerSetupProc(data, flags)
  327.     ClientData data;        /* Not used. */
  328.     int flags;            /* Event flags as passed to Tcl_DoOneEvent. */
  329. {
  330.     Tcl_Time blockTime;
  331.  
  332.     if (((flags & TCL_IDLE_EVENTS) && idleList)
  333.         || ((flags & TCL_TIMER_EVENTS) && timerPending)) {
  334.     /*
  335.      * There is an idle handler or a pending timer event, so just poll.
  336.      */
  337.  
  338.     blockTime.sec = 0;
  339.     blockTime.usec = 0;
  340.  
  341.     } else if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
  342.     /*
  343.      * Compute the timeout for the next timer on the list.
  344.      */
  345.  
  346.     TclpGetTime(&blockTime);
  347.     blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
  348.     blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
  349.     if (blockTime.usec < 0) {
  350.         blockTime.sec -= 1;
  351.         blockTime.usec += 1000000;
  352.     }
  353.     if (blockTime.sec < 0) {
  354.         blockTime.sec = 0;
  355.         blockTime.usec = 0;
  356.     }
  357.     } else {
  358.     return;
  359.     }
  360.     
  361.     Tcl_SetMaxBlockTime(&blockTime);
  362. }
  363.  
  364. /*
  365.  *----------------------------------------------------------------------
  366.  *
  367.  * TimerCheckProc --
  368.  *
  369.  *    This function is called by Tcl_DoOneEvent to check the timer
  370.  *    event source for events.  This routine checks both the
  371.  *    idle and after timer lists.
  372.  *
  373.  * Results:
  374.  *    None.
  375.  *
  376.  * Side effects:
  377.  *    May queue an event and update the maximum notifier block time.
  378.  *
  379.  *----------------------------------------------------------------------
  380.  */
  381.  
  382. static void
  383. TimerCheckProc(data, flags)
  384.     ClientData data;        /* Not used. */
  385.     int flags;            /* Event flags as passed to Tcl_DoOneEvent. */
  386. {
  387.     Tcl_Event *timerEvPtr;
  388.     Tcl_Time blockTime;
  389.  
  390.     if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
  391.     /*
  392.      * Compute the timeout for the next timer on the list.
  393.      */
  394.  
  395.     TclpGetTime(&blockTime);
  396.     blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
  397.     blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
  398.     if (blockTime.usec < 0) {
  399.         blockTime.sec -= 1;
  400.         blockTime.usec += 1000000;
  401.     }
  402.     if (blockTime.sec < 0) {
  403.         blockTime.sec = 0;
  404.         blockTime.usec = 0;
  405.     }
  406.  
  407.     /*
  408.      * If the first timer has expired, stick an event on the queue.
  409.      */
  410.  
  411.     if (blockTime.sec == 0 && blockTime.usec == 0 && !timerPending) {
  412.         timerPending = 1;
  413.         timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));
  414.         timerEvPtr->proc = TimerHandlerEventProc;
  415.         Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);
  416.     }
  417.     }
  418. }
  419.  
  420. /*
  421.  *----------------------------------------------------------------------
  422.  *
  423.  * TimerHandlerEventProc --
  424.  *
  425.  *    This procedure is called by Tcl_ServiceEvent when a timer event
  426.  *    reaches the front of the event queue.  This procedure handles
  427.  *    the event by invoking the callbacks for all timers that are
  428.  *    ready.
  429.  *
  430.  * Results:
  431.  *    Returns 1 if the event was handled, meaning it should be removed
  432.  *    from the queue.  Returns 0 if the event was not handled, meaning
  433.  *    it should stay on the queue.  The only time the event isn't
  434.  *    handled is if the TCL_TIMER_EVENTS flag bit isn't set.
  435.  *
  436.  * Side effects:
  437.  *    Whatever the timer handler callback procedures do.
  438.  *
  439.  *----------------------------------------------------------------------
  440.  */
  441.  
  442. static int
  443. TimerHandlerEventProc(evPtr, flags)
  444.     Tcl_Event *evPtr;        /* Event to service. */
  445.     int flags;            /* Flags that indicate what events to
  446.                  * handle, such as TCL_FILE_EVENTS. */
  447. {
  448.     TimerHandler *timerHandlerPtr, **nextPtrPtr;
  449.     Tcl_Time time;
  450.     int currentTimerId;
  451.  
  452.     /*
  453.      * Do nothing if timers aren't enabled.  This leaves the event on the
  454.      * queue, so we will get to it as soon as ServiceEvents() is called
  455.      * with timers enabled.
  456.      */
  457.  
  458.     if (!(flags & TCL_TIMER_EVENTS)) {
  459.     return 0;
  460.     }
  461.  
  462.     /*
  463.      * The code below is trickier than it may look, for the following
  464.      * reasons:
  465.      *
  466.      * 1. New handlers can get added to the list while the current
  467.      *    one is being processed.  If new ones get added, we don't
  468.      *    want to process them during this pass through the list to avoid
  469.      *      starving other event sources.  This is implemented using the
  470.      *      token number in the handler:  new handlers will have a
  471.      *    newer token than any of the ones currently on the list.
  472.      * 2. The handler can call Tcl_DoOneEvent, so we have to remove
  473.      *    the handler from the list before calling it. Otherwise an
  474.      *    infinite loop could result.
  475.      * 3. Tcl_DeleteTimerHandler can be called to remove an element from
  476.      *    the list while a handler is executing, so the list could
  477.      *    change structure during the call.
  478.      * 4. Because we only fetch the current time before entering the loop,
  479.      *    the only way a new timer will even be considered runnable is if
  480.      *      its expiration time is within the same millisecond as the
  481.      *      current time.  This is fairly likely on Windows, since it has
  482.      *      a course granularity clock.  Since timers are placed
  483.      *      on the queue in time order with the most recently created
  484.      *    handler appearing after earlier ones with the same expiration
  485.      *      time, we don't have to worry about newer generation timers
  486.      *      appearing before later ones.
  487.      */
  488.  
  489.     timerPending = 0;
  490.     currentTimerId = lastTimerId;
  491.     TclpGetTime(&time);
  492.     while (1) {
  493.     nextPtrPtr = &firstTimerHandlerPtr;
  494.     timerHandlerPtr = firstTimerHandlerPtr;
  495.     if (timerHandlerPtr == NULL) {
  496.         break;
  497.     }
  498.         
  499.     if ((timerHandlerPtr->time.sec > time.sec)
  500.         || ((timerHandlerPtr->time.sec == time.sec)
  501.             && (timerHandlerPtr->time.usec > time.usec))) {
  502.         break;
  503.     }
  504.  
  505.     /*
  506.      * Bail out if the next timer is of a newer generation.
  507.      */
  508.  
  509.     if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
  510.         break;
  511.     }
  512.  
  513.     /*
  514.      * Remove the handler from the queue before invoking it,
  515.      * to avoid potential reentrancy problems.
  516.      */
  517.  
  518.     (*nextPtrPtr) = timerHandlerPtr->nextPtr;
  519.     (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
  520.     ckfree((char *) timerHandlerPtr);
  521.     }
  522.     TimerSetupProc(NULL, TCL_TIMER_EVENTS);
  523.     return 1;
  524. }
  525.  
  526. /*
  527.  *--------------------------------------------------------------
  528.  *
  529.  * Tcl_DoWhenIdle --
  530.  *
  531.  *    Arrange for proc to be invoked the next time the system is
  532.  *    idle (i.e., just before the next time that Tcl_DoOneEvent
  533.  *    would have to wait for something to happen).
  534.  *
  535.  * Results:
  536.  *    None.
  537.  *
  538.  * Side effects:
  539.  *    Proc will eventually be called, with clientData as argument.
  540.  *    See the manual entry for details.
  541.  *
  542.  *--------------------------------------------------------------
  543.  */
  544.  
  545. void
  546. Tcl_DoWhenIdle(proc, clientData)
  547.     Tcl_IdleProc *proc;        /* Procedure to invoke. */
  548.     ClientData clientData;    /* Arbitrary value to pass to proc. */
  549. {
  550.     register IdleHandler *idlePtr;
  551.     Tcl_Time blockTime;
  552.  
  553.     if (!initialized) {
  554.     InitTimer();
  555.     }
  556.  
  557.     idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
  558.     idlePtr->proc = proc;
  559.     idlePtr->clientData = clientData;
  560.     idlePtr->generation = idleGeneration;
  561.     idlePtr->nextPtr = NULL;
  562.     if (lastIdlePtr == NULL) {
  563.     idleList = idlePtr;
  564.     } else {
  565.     lastIdlePtr->nextPtr = idlePtr;
  566.     }
  567.     lastIdlePtr = idlePtr;
  568.  
  569.     blockTime.sec = 0;
  570.     blockTime.usec = 0;
  571.     Tcl_SetMaxBlockTime(&blockTime);
  572. }
  573.  
  574. /*
  575.  *----------------------------------------------------------------------
  576.  *
  577.  * Tcl_CancelIdleCall --
  578.  *
  579.  *    If there are any when-idle calls requested to a given procedure
  580.  *    with given clientData, cancel all of them.
  581.  *
  582.  * Results:
  583.  *    None.
  584.  *
  585.  * Side effects:
  586.  *    If the proc/clientData combination were on the when-idle list,
  587.  *    they are removed so that they will never be called.
  588.  *
  589.  *----------------------------------------------------------------------
  590.  */
  591.  
  592. void
  593. Tcl_CancelIdleCall(proc, clientData)
  594.     Tcl_IdleProc *proc;        /* Procedure that was previously registered. */
  595.     ClientData clientData;    /* Arbitrary value to pass to proc. */
  596. {
  597.     register IdleHandler *idlePtr, *prevPtr;
  598.     IdleHandler *nextPtr;
  599.  
  600.     for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
  601.         prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
  602.     while ((idlePtr->proc == proc)
  603.         && (idlePtr->clientData == clientData)) {
  604.         nextPtr = idlePtr->nextPtr;
  605.         ckfree((char *) idlePtr);
  606.         idlePtr = nextPtr;
  607.         if (prevPtr == NULL) {
  608.         idleList = idlePtr;
  609.         } else {
  610.         prevPtr->nextPtr = idlePtr;
  611.         }
  612.         if (idlePtr == NULL) {
  613.         lastIdlePtr = prevPtr;
  614.         return;
  615.         }
  616.     }
  617.     }
  618. }
  619.  
  620. /*
  621.  *----------------------------------------------------------------------
  622.  *
  623.  * TclServiceIdle --
  624.  *
  625.  *    This procedure is invoked by the notifier when it becomes
  626.  *    idle.  It will invoke all idle handlers that are present at
  627.  *    the time the call is invoked, but not those added during idle
  628.  *    processing.
  629.  *
  630.  * Results:
  631.  *    The return value is 1 if TclServiceIdle found something to
  632.  *    do, otherwise return value is 0.
  633.  *
  634.  * Side effects:
  635.  *    Invokes all pending idle handlers.
  636.  *
  637.  *----------------------------------------------------------------------
  638.  */
  639.  
  640. int
  641. TclServiceIdle()
  642. {
  643.     IdleHandler *idlePtr;
  644.     int oldGeneration;
  645.     Tcl_Time blockTime;
  646.  
  647.     if (idleList == NULL) {
  648.     return 0;
  649.     }
  650.  
  651.     oldGeneration = idleGeneration;
  652.     idleGeneration++;
  653.  
  654.     /*
  655.      * The code below is trickier than it may look, for the following
  656.      * reasons:
  657.      *
  658.      * 1. New handlers can get added to the list while the current
  659.      *    one is being processed.  If new ones get added, we don't
  660.      *    want to process them during this pass through the list (want
  661.      *    to check for other work to do first).  This is implemented
  662.      *    using the generation number in the handler:  new handlers
  663.      *    will have a different generation than any of the ones currently
  664.      *    on the list.
  665.      * 2. The handler can call Tcl_DoOneEvent, so we have to remove
  666.      *    the handler from the list before calling it. Otherwise an
  667.      *    infinite loop could result.
  668.      * 3. Tcl_CancelIdleCall can be called to remove an element from
  669.      *    the list while a handler is executing, so the list could
  670.      *    change structure during the call.
  671.      */
  672.  
  673.     for (idlePtr = idleList;
  674.         ((idlePtr != NULL)
  675.             && ((oldGeneration - idlePtr->generation) >= 0));
  676.         idlePtr = idleList) {
  677.     idleList = idlePtr->nextPtr;
  678.     if (idleList == NULL) {
  679.         lastIdlePtr = NULL;
  680.     }
  681.     (*idlePtr->proc)(idlePtr->clientData);
  682.     ckfree((char *) idlePtr);
  683.     }
  684.     if (idleList) {
  685.     blockTime.sec = 0;
  686.     blockTime.usec = 0;
  687.     Tcl_SetMaxBlockTime(&blockTime);
  688.     }
  689.     return 1;
  690. }
  691.  
  692. /*
  693.  *----------------------------------------------------------------------
  694.  *
  695.  * Tcl_AfterObjCmd --
  696.  *
  697.  *    This procedure is invoked to process the "after" Tcl command.
  698.  *    See the user documentation for details on what it does.
  699.  *
  700.  * Results:
  701.  *    A standard Tcl result.
  702.  *
  703.  * Side effects:
  704.  *    See the user documentation.
  705.  *
  706.  *----------------------------------------------------------------------
  707.  */
  708.  
  709.     /* ARGSUSED */
  710. int
  711. Tcl_AfterObjCmd(clientData, interp, objc, objv)
  712.     ClientData clientData;    /* Points to the "tclAfter" assocData for
  713.                  * this interpreter, or NULL if the assocData
  714.                  * hasn't been created yet.*/
  715.     Tcl_Interp *interp;        /* Current interpreter. */
  716.     int objc;            /* Number of arguments. */
  717.     Tcl_Obj *CONST objv[];    /* Argument objects. */
  718. {
  719.     /*
  720.      * The variable below is used to generate unique identifiers for
  721.      * after commands.  This id can wrap around, which can potentially
  722.      * cause problems.  However, there are not likely to be problems
  723.      * in practice, because after commands can only be requested to
  724.      * about a month in the future, and wrap-around is unlikely to
  725.      * occur in less than about 1-10 years.  Thus it's unlikely that
  726.      * any old ids will still be around when wrap-around occurs.
  727.      */
  728.  
  729.     static int nextId = 1;
  730.     int ms;
  731.     AfterInfo *afterPtr;
  732.     AfterAssocData *assocPtr = (AfterAssocData *) clientData;
  733.     Tcl_CmdInfo cmdInfo;
  734.     int length;
  735.     char *arg;
  736.     int index, result;
  737.     static char *subCmds[] = {
  738.         "cancel", "idle", "info",
  739.         (char *) NULL};
  740.     
  741.     if (objc < 2) {
  742.     Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  743.     return TCL_ERROR;
  744.     }
  745.  
  746.     /*
  747.      * Create the "after" information associated for this interpreter,
  748.      * if it doesn't already exist.  Associate it with the command too,
  749.      * so that it will be passed in as the ClientData argument in the
  750.      * future.
  751.      */
  752.  
  753.     if (assocPtr == NULL) {
  754.     assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
  755.     assocPtr->interp = interp;
  756.     assocPtr->firstAfterPtr = NULL;
  757.     Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
  758.         (ClientData) assocPtr);
  759.     cmdInfo.proc = NULL;
  760.     cmdInfo.clientData = (ClientData) NULL;
  761.     cmdInfo.objProc = Tcl_AfterObjCmd;
  762.     cmdInfo.objClientData = (ClientData) assocPtr;
  763.     cmdInfo.deleteProc = NULL;
  764.     cmdInfo.deleteData = (ClientData) assocPtr;
  765.     Tcl_SetCommandInfo(interp, Tcl_GetStringFromObj(objv[0], &length),
  766.         &cmdInfo);
  767.     }
  768.  
  769.     /*
  770.      * First lets see if the command was passed a number as the first argument.
  771.      */
  772.     
  773.     arg = Tcl_GetStringFromObj(objv[1], &length);
  774.     if (isdigit(UCHAR(arg[0]))) {
  775.     if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {
  776.         return TCL_ERROR;
  777.     }
  778.     if (ms < 0) {
  779.         ms = 0;
  780.     }
  781.     if (objc == 2) {
  782.         Tcl_Sleep(ms);
  783.         return TCL_OK;
  784.     }
  785.     afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
  786.     afterPtr->assocPtr = assocPtr;
  787.     if (objc == 3) {
  788.         arg = Tcl_GetStringFromObj(objv[2], &length);
  789.         afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
  790.         strcpy(afterPtr->command, arg);
  791.     } else {
  792.         Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);
  793.         arg = Tcl_GetStringFromObj(objPtr, &length);
  794.         afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
  795.         strcpy(afterPtr->command, arg);
  796.         Tcl_DecrRefCount(objPtr);
  797.     }
  798.     afterPtr->id = nextId;
  799.     nextId += 1;
  800.     afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
  801.         (ClientData) afterPtr);
  802.     afterPtr->nextPtr = assocPtr->firstAfterPtr;
  803.     assocPtr->firstAfterPtr = afterPtr;
  804.     sprintf(interp->result, "after#%d", afterPtr->id);
  805.     return TCL_OK;
  806.     }
  807.  
  808.     /*
  809.      * If it's not a number it must be a subcommand.
  810.      */
  811.     result = Tcl_GetIndexFromObj(NULL, objv[1], subCmds, "option",
  812.             0, (int *) &index);
  813.     if (result != TCL_OK) {
  814.     Tcl_AppendResult(interp, "bad argument \"", arg,
  815.         "\": must be cancel, idle, info, or a number",
  816.         (char *) NULL);
  817.     return TCL_ERROR;
  818.     }
  819.  
  820.     switch (index) {
  821.         case 0:        /* cancel */
  822.         {
  823.         char *arg;
  824.         Tcl_Obj *objPtr = NULL;
  825.  
  826.         if (objc < 3) {
  827.             Tcl_WrongNumArgs(interp, 2, objv, "id|command");
  828.             return TCL_ERROR;
  829.         }
  830.         if (objc == 3) {
  831.             arg = Tcl_GetStringFromObj(objv[2], &length);
  832.         } else {
  833.             objPtr = Tcl_ConcatObj(objc-2, objv+2);;
  834.             arg = Tcl_GetStringFromObj(objPtr, &length);
  835.         }
  836.         for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
  837.              afterPtr = afterPtr->nextPtr) {
  838.             if (strcmp(afterPtr->command, arg) == 0) {
  839.             break;
  840.             }
  841.         }
  842.         if (afterPtr == NULL) {
  843.             afterPtr = GetAfterEvent(assocPtr, arg);
  844.         }
  845.         if (objPtr != NULL) {
  846.             Tcl_DecrRefCount(objPtr);
  847.         }
  848.         if (afterPtr != NULL) {
  849.             if (afterPtr->token != NULL) {
  850.             Tcl_DeleteTimerHandler(afterPtr->token);
  851.             } else {
  852.             Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
  853.             }
  854.             FreeAfterPtr(afterPtr);
  855.         }
  856.         break;
  857.         }
  858.     case 1:        /* idle */
  859.         if (objc < 3) {
  860.         Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
  861.         return TCL_ERROR;
  862.         }
  863.         afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
  864.         afterPtr->assocPtr = assocPtr;
  865.         if (objc == 3) {
  866.         arg = Tcl_GetStringFromObj(objv[2], &length);
  867.         afterPtr->command = (char *) ckalloc((unsigned) length + 1);
  868.         strcpy(afterPtr->command, arg);
  869.         } else {
  870.         Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);;
  871.         arg = Tcl_GetStringFromObj(objPtr, &length);
  872.         afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
  873.         strcpy(afterPtr->command, arg);
  874.         Tcl_DecrRefCount(objPtr);
  875.         }
  876.         afterPtr->id = nextId;
  877.         nextId += 1;
  878.         afterPtr->token = NULL;
  879.         afterPtr->nextPtr = assocPtr->firstAfterPtr;
  880.         assocPtr->firstAfterPtr = afterPtr;
  881.         Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
  882.         sprintf(interp->result, "after#%d", afterPtr->id);
  883.         break;
  884.     case 2:        /* info */
  885.         if (objc == 2) {
  886.         char buffer[30];
  887.         
  888.         for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
  889.              afterPtr = afterPtr->nextPtr) {
  890.             if (assocPtr->interp == interp) {
  891.             sprintf(buffer, "after#%d", afterPtr->id);
  892.             Tcl_AppendElement(interp, buffer);
  893.             }
  894.         }
  895.         return TCL_OK;
  896.         }
  897.         if (objc != 3) {
  898.         Tcl_WrongNumArgs(interp, 2, objv, "?id?");
  899.         return TCL_ERROR;
  900.         }
  901.         arg = Tcl_GetStringFromObj(objv[2], &length);
  902.         afterPtr = GetAfterEvent(assocPtr, arg);
  903.         if (afterPtr == NULL) {
  904.         Tcl_AppendResult(interp, "event \"", arg,
  905.             "\" doesn't exist", (char *) NULL);
  906.         return TCL_ERROR;
  907.         }
  908.         Tcl_AppendElement(interp, afterPtr->command);
  909.         Tcl_AppendElement(interp,
  910.             (afterPtr->token == NULL) ? "idle" : "timer");
  911.         break;
  912.     }
  913.     return TCL_OK;
  914. }
  915.  
  916. /*
  917.  *----------------------------------------------------------------------
  918.  *
  919.  * GetAfterEvent --
  920.  *
  921.  *    This procedure parses an "after" id such as "after#4" and
  922.  *    returns a pointer to the AfterInfo structure.
  923.  *
  924.  * Results:
  925.  *    The return value is either a pointer to an AfterInfo structure,
  926.  *    if one is found that corresponds to "string" and is for interp,
  927.  *    or NULL if no corresponding after event can be found.
  928.  *
  929.  * Side effects:
  930.  *    None.
  931.  *
  932.  *----------------------------------------------------------------------
  933.  */
  934.  
  935. static AfterInfo *
  936. GetAfterEvent(assocPtr, string)
  937.     AfterAssocData *assocPtr;    /* Points to "after"-related information for
  938.                  * this interpreter. */
  939.     char *string;        /* Textual identifier for after event, such
  940.                  * as "after#6". */
  941. {
  942.     AfterInfo *afterPtr;
  943.     int id;
  944.     char *end;
  945.  
  946.     if (strncmp(string, "after#", 6) != 0) {
  947.     return NULL;
  948.     }
  949.     string += 6;
  950.     id = strtoul(string, &end, 10);
  951.     if ((end == string) || (*end != 0)) {
  952.     return NULL;
  953.     }
  954.     for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
  955.         afterPtr = afterPtr->nextPtr) {
  956.     if (afterPtr->id == id) {
  957.         return afterPtr;
  958.     }
  959.     }
  960.     return NULL;
  961. }
  962.  
  963. /*
  964.  *----------------------------------------------------------------------
  965.  *
  966.  * AfterProc --
  967.  *
  968.  *    Timer callback to execute commands registered with the
  969.  *    "after" command.
  970.  *
  971.  * Results:
  972.  *    None.
  973.  *
  974.  * Side effects:
  975.  *    Executes whatever command was specified.  If the command
  976.  *    returns an error, then the command "bgerror" is invoked
  977.  *    to process the error;  if bgerror fails then information
  978.  *    about the error is output on stderr.
  979.  *
  980.  *----------------------------------------------------------------------
  981.  */
  982.  
  983. static void
  984. AfterProc(clientData)
  985.     ClientData clientData;    /* Describes command to execute. */
  986. {
  987.     AfterInfo *afterPtr = (AfterInfo *) clientData;
  988.     AfterAssocData *assocPtr = afterPtr->assocPtr;
  989.     AfterInfo *prevPtr;
  990.     int result;
  991.     Tcl_Interp *interp;
  992.  
  993.     /*
  994.      * First remove the callback from our list of callbacks;  otherwise
  995.      * someone could delete the callback while it's being executed, which
  996.      * could cause a core dump.
  997.      */
  998.  
  999.     if (assocPtr->firstAfterPtr == afterPtr) {
  1000.     assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1001.     } else {
  1002.     for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
  1003.         prevPtr = prevPtr->nextPtr) {
  1004.         /* Empty loop body. */
  1005.     }
  1006.     prevPtr->nextPtr = afterPtr->nextPtr;
  1007.     }
  1008.  
  1009.     /*
  1010.      * Execute the callback.
  1011.      */
  1012.  
  1013.     interp = assocPtr->interp;
  1014.     Tcl_Preserve((ClientData) interp);
  1015.     result = Tcl_GlobalEval(interp, afterPtr->command);
  1016.     if (result != TCL_OK) {
  1017.     Tcl_AddErrorInfo(interp, "\n    (\"after\" script)");
  1018.     Tcl_BackgroundError(interp);
  1019.     }
  1020.     Tcl_Release((ClientData) interp);
  1021.     
  1022.     /*
  1023.      * Free the memory for the callback.
  1024.      */
  1025.  
  1026.     ckfree(afterPtr->command);
  1027.     ckfree((char *) afterPtr);
  1028. }
  1029.  
  1030. /*
  1031.  *----------------------------------------------------------------------
  1032.  *
  1033.  * FreeAfterPtr --
  1034.  *
  1035.  *    This procedure removes an "after" command from the list of
  1036.  *    those that are pending and frees its resources.  This procedure
  1037.  *    does *not* cancel the timer handler;  if that's needed, the
  1038.  *    caller must do it.
  1039.  *
  1040.  * Results:
  1041.  *    None.
  1042.  *
  1043.  * Side effects:
  1044.  *    The memory associated with afterPtr is released.
  1045.  *
  1046.  *----------------------------------------------------------------------
  1047.  */
  1048.  
  1049. static void
  1050. FreeAfterPtr(afterPtr)
  1051.     AfterInfo *afterPtr;        /* Command to be deleted. */
  1052. {
  1053.     AfterInfo *prevPtr;
  1054.     AfterAssocData *assocPtr = afterPtr->assocPtr;
  1055.  
  1056.     if (assocPtr->firstAfterPtr == afterPtr) {
  1057.     assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1058.     } else {
  1059.     for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
  1060.         prevPtr = prevPtr->nextPtr) {
  1061.         /* Empty loop body. */
  1062.     }
  1063.     prevPtr->nextPtr = afterPtr->nextPtr;
  1064.     }
  1065.     ckfree(afterPtr->command);
  1066.     ckfree((char *) afterPtr);
  1067. }
  1068.  
  1069. /*
  1070.  *----------------------------------------------------------------------
  1071.  *
  1072.  * AfterCleanupProc --
  1073.  *
  1074.  *    This procedure is invoked whenever an interpreter is deleted
  1075.  *    to cleanup the AssocData for "tclAfter".
  1076.  *
  1077.  * Results:
  1078.  *    None.
  1079.  *
  1080.  * Side effects:
  1081.  *    After commands are removed.
  1082.  *
  1083.  *----------------------------------------------------------------------
  1084.  */
  1085.  
  1086.     /* ARGSUSED */
  1087. static void
  1088. AfterCleanupProc(clientData, interp)
  1089.     ClientData clientData;    /* Points to AfterAssocData for the
  1090.                  * interpreter. */
  1091.     Tcl_Interp *interp;        /* Interpreter that is being deleted. */
  1092. {
  1093.     AfterAssocData *assocPtr = (AfterAssocData *) clientData;
  1094.     AfterInfo *afterPtr;
  1095.  
  1096.     while (assocPtr->firstAfterPtr != NULL) {
  1097.     afterPtr = assocPtr->firstAfterPtr;
  1098.     assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1099.     if (afterPtr->token != NULL) {
  1100.         Tcl_DeleteTimerHandler(afterPtr->token);
  1101.     } else {
  1102.         Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
  1103.     }
  1104.     ckfree(afterPtr->command);
  1105.     ckfree((char *) afterPtr);
  1106.     }
  1107.     ckfree((char *) assocPtr);
  1108. }
  1109.